//-----------------------------------------------------------------------------
//  Copyright (c) 2013 by HangZhou HenqgQiao Design Corporation. All rights reserved.
//
//  Project  : 
//  Module   : 
//  Parent   : 
//  Children : 
//
//  Description: 
//
//  Parameters:
//  Local Parameters:
//
//  Notes       : 
//
//  Multicycle and False Paths


module AUPP_RGFSM(
   input                      GTM_AUPP_RESET,
   input                      GTM_SYS_CKOCK,

   input[2:0]                 FMR_IN_FCNT8,
   input[8:0]                 FMR_IN_FCNT270,
   input[3:0]                 FMR_IN_FCNT9,
   input[1:0]                 FMR_IN_PSCNT3,
   input[9:0]                 FMR_IN_PSCNT783,
   input                      FMR_IN_HOSPE,
   input[23:0]                FMR_IN_DATA,
   input                      FMR_IN_AIS,
   input                      FMR_IN_J1,
   input[1:0]                 FMR_IN_FIFO_STATUS,    // FIFO status, 0x0---almost empty; 0x1---normal; 0x2---almost full; 0x3---fifo error

   output[2:0]                SPECT_OUT_CHNN,
   output reg                 SPECT_OUT_EN,
   output[1:0]                SPECT_OUT_MODE         // SPE generate control bits, 00--Normal     01--point increase, bytes after H3 not data    10-- point decrease, H3 will be data
   );


reg[2:0]                      FSM_RCHNN;

wire[2:0]                     FSM_FCNT8;
wire[8:0]                     FSM_FCNT270;
wire[3:0]                     FSM_FCNT9;
wire[9:0]                     FSM_PSCNT783;
wire[1:0]                     FSM_PSCNT3;
wire                          FSM_HOSPE;
wire                          FSM_J1;
wire                          FSM_AIS;
wire[1:0]                     FSM_FIFO_STATUS;

wire[3:0]                     FSM_STATE;
wire[9:0]                     FSM_PNVALUE;            // current point value
wire[9:0]                     FSM_J1PS;               // last J1 position on 
wire                          FSM_EN;
wire                          FSM_J1MISMATCH;

reg[2:0]                      FSM_WR_CHNN;
reg[3:0]                      FSM_WR_STATE;
reg[9:0]                      FSM_WR_PNVALUE;
reg[9:0]                      FSM_WR_J1PS;
reg                           FSM_WR_EN;
reg                           FSM_WR_J1MISMATCH;

wire                          FSM_RAM_CLKA, FSM_RAM_CLKB;
wire                          FSM_RAM_WEA;
wire[2:0]                     FSM_RAM_ADDRA, FSM_RAM_ADDRB;
wire[24:0]                    FSM_RAM_DINA, FSM_RAM_DOUTB;

parameter                     C_FSM_NORMAL       = 4'd0;
parameter                     C_FSM_INC_0        = 4'd1;
parameter                     C_FSM_INC_1        = 4'd2;
parameter                     C_FSM_INC_2        = 4'd3;
parameter                     C_FSM_INC_3        = 4'd4;
parameter                     C_FSM_DEC_0        = 4'd5;
parameter                     C_FSM_DEC_1        = 4'd6;
parameter                     C_FSM_DEC_2        = 4'd7;
parameter                     C_FSM_DEC_3        = 4'd8;
parameter                     C_FSM_NDF_0        = 4'd9;
parameter                     C_FSM_NDF_1        = 4'd10;
parameter                     C_FSM_NDF_2        = 4'd11;
parameter                     C_FSM_NDF_3        = 4'd12;
parameter                     C_FSM_AIS          = 4'd13;



  assign FSM_FCNT8[2:0]        = FMR_IN_FCNT8[2:0];
  assign FSM_FCNT270[8:0]      = FMR_IN_FCNT270[8:0];
  assign FSM_FCNT9[3:0]        = FMR_IN_FCNT9[3:0];
  assign FSM_PSCNT783[9:0]     = FMR_IN_PSCNT783[9:0];
  assign FSM_PSCNT3[1:0]       = FMR_IN_PSCNT3[1:0];
  assign FSM_HOSPE             = FMR_IN_HOSPE;
  assign FSM_J1                = FMR_IN_J1;
  assign FSM_AIS               = FMR_IN_AIS;
  assign FSM_FIFO_STATUS[1:0]  = FMR_IN_FIFO_STATUS[1:0];

// generate RAM read address, shift 2 clock cycle from FMR_IN_FCNT8
always @( posedge GTM_AUPP_RESET or posedge GTM_SYS_CKOCK ) begin
   if ( GTM_AUPP_RESET==1'b1 )
      FSM_RCHNN[2:0]                               <= 3'd0;
   else begin
      if ( FSM_FCNT8[2:0]==3'd0 )
         FSM_RCHNN[2:0]                            <= 3'd3;
      else
         FSM_RCHNN[2:0]                            <= FSM_RCHNN[2:0] +3'd1;
   end
end

always @( posedge GTM_AUPP_RESET or posedge GTM_SYS_CKOCK ) begin
   if ( GTM_AUPP_RESET==1'b1 )
      FSM_WR_J1MISMATCH                            <= 1'b0;
   else begin
      if ( FSM_J1==1'b1 && FSM_HOSPE==1'b1 && FSM_PSCNT3[1:0]==2'd2 ) begin
         if ( FSM_PSCNT783[9:0]!=FSM_PNVALUE[9:0] )
            FSM_WR_J1MISMATCH                      <= 1'b1;
         else
            FSM_WR_J1MISMATCH                      <= 1'b0;
      end
      else begin
            FSM_WR_J1MISMATCH                      <= FSM_J1MISMATCH;
      end
   end
end

always @( posedge GTM_AUPP_RESET or posedge GTM_SYS_CKOCK ) begin
   if ( GTM_AUPP_RESET==1'b1 )
      FSM_WR_J1PS[9:0]                             <= 10'd0;
   else begin
      if ( FSM_J1==1'b1 && FSM_HOSPE==1'b1 && FSM_PSCNT3[1:0]==2'd2 )
         FSM_WR_J1PS[9:0]                          <= FSM_PSCNT783[9:0];
      else
         FSM_WR_J1PS[9:0]                          <= FSM_J1PS[9:0];
   end
end

  assign FSM_EN   = ( FSM_FCNT270[8:0]==9'd269 ) && ( FSM_FCNT9[3:0]==4'd3 );
always @( posedge GTM_AUPP_RESET or posedge GTM_SYS_CKOCK ) begin
   if ( GTM_AUPP_RESET==1'b1 ) begin
      FSM_WR_STATE[3:0]                            <= C_FSM_NORMAL;
      FSM_WR_PNVALUE[9:0]                          <= 10'd0;
   end
   else begin
      if ( FSM_EN==1'b1 ) begin
         case ( FSM_STATE[3:0] )
         C_FSM_NORMAL : begin
            FSM_WR_PNVALUE[9:0]                    <= FSM_PNVALUE[9:0];            // no point change at the normal state
            if ( FSM_FIFO_STATUS[1:0]==2'd0 )            // FIFO almost empty, increase the point
               FSM_WR_STATE[3:0]                   <= C_FSM_INC_0;
            else if ( FSM_FIFO_STATUS[1:0]==2'd2 )       // FIFO almost full, decrease the point
               FSM_WR_STATE[3:0]                   <= C_FSM_DEC_0;
            else if ( FSM_J1MISMATCH==1'b1 )               // J1 position error
               FSM_WR_STATE[3:0]                   <= C_FSM_NDF_0;
            else if ( FSM_AIS==1'b1 )
               FSM_WR_STATE[3:0]                   <= C_FSM_AIS;
            else
               FSM_WR_STATE[3:0]                   <= C_FSM_NORMAL;
         end

         C_FSM_INC_0 : begin
               FSM_WR_STATE[3:0]                   <= C_FSM_INC_1;
               FSM_WR_PNVALUE[9:0]                 <= FSM_PNVALUE[9:0];
         end
         C_FSM_INC_1 : begin
               FSM_WR_STATE[3:0]                   <= C_FSM_INC_2;
               if ( FSM_PNVALUE[9:0]==10'd782 )
                  FSM_WR_PNVALUE[9:0]              <= 10'd0;
               else
                  FSM_WR_PNVALUE[9:0]              <= FSM_PNVALUE[9:0] +10'd1;
         end
         C_FSM_INC_2 : begin
               FSM_WR_STATE[3:0]                   <= C_FSM_INC_3;
               FSM_WR_PNVALUE[9:0]                 <= FSM_PNVALUE[9:0];
         end
         C_FSM_INC_3 : begin
               FSM_WR_STATE[3:0]                   <= C_FSM_NORMAL;
               FSM_WR_PNVALUE[9:0]                 <= FSM_PNVALUE[9:0];
         end

         C_FSM_DEC_0 : begin
               FSM_WR_STATE[3:0]                   <= C_FSM_DEC_1;
               FSM_WR_PNVALUE[9:0]                 <= FSM_PNVALUE[9:0];
         end
         C_FSM_DEC_1 : begin
               FSM_WR_STATE[3:0]                   <= C_FSM_DEC_2;
               if ( FSM_PNVALUE[9:0]==10'd0 )
                  FSM_WR_PNVALUE[9:0]              <= 10'd782;
               else
                  FSM_WR_PNVALUE[9:0]              <= FSM_PNVALUE[9:0] -10'd1;
         end
         C_FSM_DEC_2 : begin
               FSM_WR_STATE[3:0]                   <= C_FSM_DEC_3;
               FSM_WR_PNVALUE[9:0]                 <= FSM_PNVALUE[9:0];
         end
         C_FSM_DEC_3 : begin
               FSM_WR_STATE[3:0]                   <= C_FSM_NORMAL;
               FSM_WR_PNVALUE[9:0]                 <= FSM_PNVALUE[9:0];
         end

         C_FSM_NDF_0 : begin
               FSM_WR_STATE[3:0]                   <= C_FSM_NDF_1;
               FSM_WR_PNVALUE[9:0]                 <= FSM_J1PS[9:0];
         end
         C_FSM_NDF_1 : begin
               FSM_WR_STATE[3:0]                   <= C_FSM_NDF_2;
               FSM_WR_PNVALUE[9:0]                 <= FSM_PNVALUE[9:0];
         end
         C_FSM_NDF_2 : begin
               FSM_WR_STATE[3:0]                   <= C_FSM_NDF_3;
               FSM_WR_PNVALUE[9:0]                 <= FSM_PNVALUE[9:0];
         end
         C_FSM_NDF_3 : begin
               FSM_WR_STATE[3:0]                   <= C_FSM_NORMAL;
               FSM_WR_PNVALUE[9:0]                 <= FSM_PNVALUE[9:0];
         end

         C_FSM_AIS   : begin
            if ( FSM_AIS==1'b0 )
               FSM_WR_STATE[3:0]                   <= C_FSM_NORMAL;
               FSM_WR_PNVALUE[9:0]                 <= FSM_PNVALUE[9:0];
         end

         default: begin
               FSM_WR_STATE[3:0]                   <= C_FSM_NORMAL;
               FSM_WR_PNVALUE[9:0]                 <= FSM_PNVALUE[9:0];
         end
         endcase
      end
      else begin
         FSM_WR_STATE[3:0]                         <= FSM_STATE[3:0];
      end
   end
end


always @( posedge GTM_AUPP_RESET or posedge GTM_SYS_CKOCK ) begin
   if ( GTM_AUPP_RESET==1'b1 )
      FSM_WR_CHNN[2:0]                             <= 3'd0;
   else
      FSM_WR_CHNN[2:0]                             <= FSM_FCNT8[2:0];
end

// FSM RAM installation
  assign  FSM_RAM_CLKA          = GTM_SYS_CKOCK;
  assign  FSM_RAM_WEA           = 1'b1;              // always write
  assign  FSM_RAM_ADDRA[2:0]    = FSM_WR_CHNN[2:0];
  assign  FSM_RAM_DINA[9:0]     = FSM_WR_PNVALUE[9:0];
  assign  FSM_RAM_DINA[19:10]   = FSM_WR_J1PS[9:0];
  assign  FSM_RAM_DINA[23:20]   = FSM_WR_STATE[3:0];
  assign  FSM_RAM_DINA[24]      = FSM_WR_J1MISMATCH;

  assign  FSM_RAM_CLKB          = GTM_SYS_CKOCK;
  assign  FSM_RAM_ADDRB[2:0]    = FSM_RCHNN[2:0];
  assign  FSM_PNVALUE[9:0]      = FSM_RAM_DOUTB[9:0];
  assign  FSM_J1PS[9:0]         = FSM_RAM_DOUTB[19:10];
  assign  FSM_STATE[3:0]        = FSM_RAM_DOUTB[23:20];
  assign  FSM_J1MISMATCH        = FSM_RAM_DOUTB[24];

AURG_FSM_RAM200_25_25                        INST_FSM_RAM200_25_25(
   .CLKA                                     ( FSM_RAM_CLKA ),
   .WEA                                      ( FSM_RAM_WEA ),
   .ADDRA                                    ( FSM_RAM_ADDRA[2:0] ),
   .DINA                                     ( FSM_RAM_DINA[24:0] ),

   .CLKB                                     ( FSM_RAM_CLKB ),
   .ADDRB                                    ( FSM_RAM_ADDRB[2:0] ),
   .DOUTB                                    ( FSM_RAM_DOUTB[24:0] )
   );

// output spe control signals to SPECT interface
always @( posedge GTM_AUPP_RESET or posedge GTM_SYS_CKOCK ) begin
   if ( GTM_AUPP_RESET==1'b1 )
      SPECT_OUT_EN                           <= 1'b0;
   else
      SPECT_OUT_EN                           <= FSM_EN;
end
  assign SPECT_OUT_CHNN[2:0]        = FSM_WR_CHNN[2:0];
  assign SPECT_OUT_MODE[0]          = FSM_WR_STATE[3:0]==C_FSM_INC_0;         // the first increase i-bit invert frame
  assign SPECT_OUT_MODE[1]          = FSM_WR_STATE[3:0]==C_FSM_DEC_0;         // the first increase d-bit invert frame


endmodule
